use super::proto::types;
use crate::error::{error_of, AppErr, AppErrMap};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use tokio::{
    sync::{mpsc, Mutex, MutexGuard},
    time::timeout,
};

#[derive(Debug, Deserialize)]
pub struct TaskInfo {
    pub dst: u8,
    pub seq: u8,
    pub cmd: u8,
}

#[derive(Debug, Serialize, Clone)]
pub struct Message {
    pub buf: Box<[u8]>,
}

impl Message {
    pub fn new(buf: Box<[u8]>) -> Self {
        Self { buf }
    }

    pub fn print(&self) {
        print!("recv: ");
        let buf: &[u8] = &self.buf;
        for v in buf {
            print!("{:02x} ", v);
        }
        println!();
    }

    pub fn parse_res_u8(&self) -> Result<u8, AppErr> {
        let len = self.buf.len();
        if len < 11 {
            return error_of("req info len < 11");
        }
        Ok(self.buf[8])
    }

    pub fn res_assert(&self) -> Result<(), AppErr> {
        let value = self.parse_res_u8()?;
        if value == 0 {
            return Ok(());
        }
        Err(AppErr::Dyn(format!("err code: {}", value)))
    }

    pub fn parse_task_info(&self) -> Result<TaskInfo, AppErr> {
        let len = self.buf.len();
        if len < 11 {
            return error_of("req info len < 11");
        }
        let info = TaskInfo {
            dst: self.dst(),
            seq: self.buf[7],
            cmd: self.buf[8],
        };
        Ok(info)
    }

    #[inline]
    pub fn src(&self) -> u8 {
        self.buf[3]
    }

    #[inline]
    pub fn dst(&self) -> u8 {
        self.buf[4]
    }

    #[inline]
    pub fn types(&self) -> u8 {
        self.buf[5]
    }

    #[inline]
    pub fn seq(&self) -> u8 {
        self.buf[6]
    }

    #[inline]
    pub fn query_result_cmd(&self) -> u8 {
        self.buf[6]
    }

    #[inline]
    pub fn req_res_notify_ack_cmd(&self) -> u8 {
        self.buf[7]
    }
}

pub struct RecvQueue {
    pub tx: mpsc::Sender<Message>,
    pub rx: Mutex<mpsc::Receiver<Message>>,
}

pub type MutRx<'a> = MutexGuard<'a, mpsc::Receiver<Message>>;

impl RecvQueue {
    pub fn new() -> Self {
        let (tx, rx) = mpsc::channel(10);
        Self {
            tx,
            rx: Mutex::new(rx),
        }
    }

    pub async fn notify(&self, msg: Message) {
        self.tx.send(msg).await.print_if_err();
    }

    pub async fn get_rx<'a>(&'a self) -> MutRx<'a> {
        self.rx.lock().await
    }
}

pub async fn wait_result<'a>(rx: &mut MutRx<'a>, cmd: u8) -> Result<Message, AppErr> {
    for _ in 0..3 {
        let result = timeout(Duration::from_millis(50), rx.recv()).await;

        let result = match result {
            Ok(v) => v,
            Err(_) => continue,
        };

        let result = match result {
            Some(v) => v,
            None => {
                return error_of("接收错误 None");
            }
        };

        if (result.types() == types::RESULT) && (result.query_result_cmd() == cmd) {
            return Ok(result);
        }
    }

    return error_of("无法收到Result");
}

pub async fn wait_pong<'a>(rx: &mut MutRx<'a>) -> Result<(), AppErr> {
    for _ in 0..3 {
        let result = timeout(Duration::from_millis(50), rx.recv()).await;

        let result = match result {
            Ok(v) => v,
            Err(_) => continue,
        };

        let result = match result {
            Some(v) => v,
            None => {
                return error_of("接收错误 None");
            }
        };

        if result.types() == types::PONG {
            return Ok(());
        }
    }

    return error_of("无法收到Pong");
}

pub async fn wait_ack<'a>(rx: &mut MutRx<'a>, seq: u8) -> Result<(), AppErr> {
    for _ in 0..3 {
        let result = timeout(Duration::from_millis(100), rx.recv()).await;

        let result = match result {
            Ok(v) => v,
            Err(_) => continue,
        };

        let result = match result {
            Some(v) => v,
            None => {
                return error_of("接收错误 None");
            }
        };

        if (result.types() == types::ACK) && (result.seq() == seq) {
            return Ok(());
        }
    }

    return error_of("无法收到ACK");
}
